﻿using System;



namespace Crocodile.AbstractLanguage.Tree
{
    /// <summary>
    /// Abstract node for a program structure tree.
    /// </summary>
    public abstract class AbstractNode
    {
        /// <summary>
        /// The parent node.
        /// </summary>
        public AbstractNode Parent { get; internal set; }


        /// <summary>
        /// Additional information associated with this node.
        /// </summary>
        public object Tag;


        /// <summary>
        /// Returns the nearest ancestor that is an instance of the specified class.
        /// </summary>
        /// <typeparam name="T">class of node to return.</typeparam>
        /// <returns>found ncestor or self or null if not found.</returns>
        public T Ancestor<T> ()
            where T : AbstractNode
        {
            AbstractNode node = Parent;
            while (node != null && !(node is T))
                node = node.Parent;
            return node != null ? node as T : null;
        }


        /// <summary>
        /// Returns the nearest ancestor or self that is an instance of the specified class
        /// and meets the specified predicate.
        /// </summary>
        /// <typeparam name="T">class of node to return.</typeparam>
        /// <param name="predicate">predicate to check nodes.</param>
        /// <returns>found ncestor or self or null if not found.</returns>
        public T Ancestor<T> (Predicate<T> predicate)
            where T : AbstractNode
        {
            AbstractNode node = Parent;
            while (node != null && !(node is T && predicate(node as T)))
                node = node.Parent;
            return node != null ? node as T : null;
        }


        /// <summary>
        /// Returns the nearest ancestor or self that is an instance of the specified class.
        /// </summary>
        /// <typeparam name="T">class of node to return.</typeparam>
        /// <returns>found ncestor or self or null if not found.</returns>
        public T AncestorOrSelf<T> ()
            where T : AbstractNode
        {
            AbstractNode node = this;
            while (node != null && !(node is T))
                node = node.Parent;
            return node != null ? node as T : null;
        }


        /// <summary>
        /// Returns the nearest ancestor or self that is an instance of the specified class
        /// and meets the specified predicate.
        /// </summary>
        /// <typeparam name="T">class of node to return.</typeparam>
        /// <param name="predicate">predicate to check nodes.</param>
        /// <returns>found ncestor or self or null if not found.</returns>
        public T AncestorOrSelf<T> (Predicate<T> predicate)
            where T : AbstractNode
        {
            AbstractNode node = this;
            while (node != null && !(node is T && predicate(node as T)))
                node = node.Parent;
            return node != null ? node as T : null;
        }


        public static bool operator true (AbstractNode node)
        {
            return node != null;
        }

        public static bool operator false (AbstractNode node)
        {
            return node == null;
        }

    }
}
